package webx.json;

import stdx.Utils;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.lang.reflect.Field;

public class JsonObject{
	String val = null;
	com.google.gson.JsonArray array = null;
	com.google.gson.JsonObject json = null;
	static com.google.gson.GsonBuilder factory = new com.google.gson.GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss");

	public JsonObject(){
	}
	public JsonObject(String msg){
		try{
			this.parse(msg);
		}
		catch(Exception e){
		}
	}
	public JsonObject(com.google.gson.JsonObject obj){
		this.json = obj;
	}
	public JsonObject(com.google.gson.JsonElement elem){
		if (elem.isJsonObject()){
			this.json = elem.getAsJsonObject();
		}
		else if (elem.isJsonArray()){
			this.array = elem.getAsJsonArray();
		}
		else if (elem.isJsonPrimitive()){
			this.val = elem.getAsString();
		}
	}

	public String toString(){
		if (val != null) return val;
		if (json != null) return json.toString();
		if (array != null) return array.toString();
		return null;
	}
	@SuppressWarnings("unchecked")
	public <T> T toObject(Class<T> clazz){
		if (clazz == String.class) return (T)toString();
		if (json != null) return factory.create().fromJson(json, clazz);
		return array == null ? null : factory.create().fromJson(array, clazz);
	}
	@SuppressWarnings("unchecked")
	public <T> List<T> toList(Class<T> clazz){
		return array == null ? null : factory.create().fromJson(array, new ArrayList<T>().getClass());
	}
	public void parse(String msg) throws Exception{
		val = null;
		json = null;
		array = null;

		com.google.gson.JsonElement elem = new com.google.gson.JsonParser().parse(msg);

		if (elem.isJsonPrimitive()) val = elem.getAsString();
		else if (elem.isJsonArray()) array = (com.google.gson.JsonArray)(elem);
		else if (elem.isJsonObject()) json = (com.google.gson.JsonObject)(elem);
	}

	public int size(){
		return array.size();
	}
	public boolean has(int idx){
		return array != null && array.size() > idx;
	}
	public boolean has(String key){
		return json != null && json.has(key);
	}
	public JsonObject get(int idx){
		if (array == null || idx >= array.size()) return null;
		return new JsonObject(array.get(idx));
	}
	public JsonObject get(String key){
		return has(key) ? new JsonObject(json.get(key)) : null;
	}

	public boolean isNull(){
		return val == null && json == null && array == null;
	}
	public boolean isValue(){
		return val != null;
	}
	public boolean isArray(){
		return array != null;
	}
	public boolean isObject(){
		return json != null;
	}
	public boolean isNull(int idx){
		return has(idx) ? get(idx).isNull() : true;
	}
	public boolean isValue(int idx){
		return has(idx) ? get(idx).isValue() : false;
	}
	public boolean isArray(int idx){
		return has(idx) ? get(idx).isArray() : false;
	}
	public boolean isObject(int idx){
		return has(idx) ? get(idx).isObject() : false;
	}
	public boolean isNull(String key){
		return has(key) ? json.get(key).isJsonNull() : true;
	}
	public boolean isValue(String key){
		return has(key) ? json.get(key).isJsonPrimitive() : false;
	}
	public boolean isArray(String key){
		return has(key) ? json.get(key).isJsonArray() : false;
	}
	public boolean isObject(String key){
		return has(key) ? json.get(key).isJsonObject() : false;
	}

	public int asInt(int idx){
		String val = asString(idx);
		if (val == null || val.isEmpty()) return 0;
		return Integer.parseInt(val);
	}
	public long asLong(int idx){
		String val = asString(idx);
		if (val == null || val.isEmpty()) return 0;
		return Long.parseLong(val);
	}
	public String asString(int idx){
		JsonObject obj = get(idx);
		if (obj == null) return null;
		if (obj.isValue()) return obj.toString();
		return null;
	}
	public double asDouble(int idx){
		String val = asString(idx);
		if (val == null || val.isEmpty()) return 0;
		return Double.parseDouble(val);
	}

	public int asInt(String key){
		String val = asString(key);
		if (val == null || val.isEmpty()) return 0;
		return Integer.parseInt(val);
	}
	public long asLong(String key){
		String val = asString(key);
		if (val == null || val.isEmpty()) return 0;
		return Long.parseLong(val);
	}
	public String asString(String key){
		if (has(key)){
			com.google.gson.JsonElement tmp = json.get(key);
			if (tmp.isJsonPrimitive()) return tmp.getAsString();
		}
		return null;
	}
	public double asDouble(String key){
		String val = asString(key);
		if (val == null || val.isEmpty()) return 0;
		return Double.parseDouble(val);
	}

	public void addNull(){
		array.add((String)(null));
	}
	public void add(Date val){
		if (val == null){
			addNull();
		}
		else{
			add(Utils.GetDateTimeString(val));
		}
	}
	public void add(String val){
		array.add(val);
	}
	public void add(Number val){
		array.add(val);
	}
	public void add(Boolean val){
		array.add(val);
	}
	public void add(Character val){
		array.add(val);
	}
	public void add(JsonObject val){
		if (isArray()){
			if (val == null || val.isNull()){
				addNull();
			}
			else{
				if (val.isValue()) add(val.val);
				else if (val.isArray()) array.add(val.array);
				else if (val.isObject()) array.add(val.json);
			}
		}
	}
	public void remove(String key){
		if (has(key)) json.remove(key);
	}
	public JsonObject addNull(String key){
		json.add(key, null);
		return get(key);
	}
	public JsonObject addObject(String key){
		JsonObject item = new JsonObject(new com.google.gson.JsonParser().parse("{}"));
		json.add(key, item.json);
		return item;
	}
	public JsonObject addArray(String key){
		JsonObject item = new JsonObject(new com.google.gson.JsonParser().parse("[]"));
		json.add(key, item.array);
		return item;
	}

	public void put(String key, Date val){
		json.addProperty(key, val == null ? null : Utils.GetDateTimeString(val));
	}

	public void put(String key, String val){
		json.addProperty(key, val);
	}
	public void put(String key, Number val){
		json.addProperty(key, val);
	}
	public void put(String key, Boolean val){
		json.addProperty(key, val);
	}
	public void put(String key, Character val){
		json.addProperty(key, val);
	}
	public JsonObject put(String key, JsonObject val){
		if (json.has(key)) json.remove(key);
		if (val.isArray()) json.add(key, val.array);
		else if (val.isObject()) json.add(key, val.json);
		else if (val.isValue()) put(key, val.val);
		return new JsonObject(json.get(key));
	}

	public static JsonObject FromObject(Object obj) throws IllegalAccessException{
		if (obj == null) return new JsonObject();

		if (obj instanceof Collection){
			Collection vec = (Collection)(obj);
			JsonObject json = new JsonObject("[]");

			for (Object item : vec){
				if (item == null){
					json.addNull();
				}
				else if (item instanceof String){
					json.add((String)(item));
				}
				else if (item instanceof Integer){
					json.add((Integer)(item));
				}
				else if (item instanceof Double){
					json.add((Double)(item));
				}
				else if (item instanceof Boolean){
					json.add((Boolean)(item));
				}
				else if (item instanceof Long){
					Long val = (Long)(item);
					if (val < Integer.MIN_VALUE || val > Integer.MAX_VALUE){
						json.add(val.toString());
					}
					else{
						json.add(val);
					}
				}
				else if (item instanceof Short){
					json.add((Short)(item));
				}
				else if (item instanceof Float){
					json.add((Float)(item));
				}
				else if (item instanceof Byte){
					json.add((Byte)(item));
				}
				else if (item instanceof Date){
					json.add((Date)(item));
				}
				else if (item instanceof Character){
					json.add(String.valueOf(item));
				}
				else{
					json.add(FromObject(item));
				}
			}

			return json;
		}
		else {
			JsonObject json = new JsonObject("{}");
			Field[] fields = obj.getClass().getFields();

			for (Field field : fields) {
				String name = field.getName();
				String type = field.getGenericType().toString();

				field.setAccessible(true);

				Object item = field.get(obj);

				if (item == null){
					json.addNull(name);
				}
				else if (item instanceof String){
					json.put(name, (String)(item));
				}
				else if (item instanceof Integer){
					json.put(name, (Integer)(item));
				}
				else if (item instanceof Double){
					json.put(name, (Double)(item));
				}
				else if (item instanceof Boolean){
					json.put(name, (Boolean)(item));
				}
				else if (item instanceof Long){
					Long val = (Long)(item);
					if (val < Integer.MIN_VALUE || val > Integer.MAX_VALUE){
						json.put(name, val.toString());
					}
					else{
						json.put(name, val);
					}
				}
				else if (item instanceof Short){
					json.put(name, (Short)(item));
				}
				else if (item instanceof Float){
					json.put(name, (Float)(item));
				}
				else if (item instanceof Byte){
					json.put(name, (Byte)(item));
				}
				else if (item instanceof Date){
					json.put(name, (Date)(item));
				}
				else if (item instanceof Character){
					json.put(name, String.valueOf(item));
				}
				else{
					json.put(name, FromObject(item));
				}
			}

			return json;
		}
	}
}